package edu.ucla.cs.rpc.multicast.network;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

import edu.ucla.cs.rpc.multicast.handlers.MessageHandler;
import edu.ucla.cs.rpc.multicast.network.message.Message;

/**
 * A MessageReceiver starts a Thread to listen on a socket for incoming TCP
 * connections. Each connection transfers one serialized Message across the
 * network. Upon receipt, the MessageReceiver invokes a MessageHandler upon the
 * Message and begins listening for another connection.
 * 
 * @author Chase Covello Philip Russell
 * 
 */
public class MessageReceiver {

	/**
	 * UpdateThread creates a server socket and listens for incoming network
	 * connections. It deserializes a Message from the connection and hands it
	 * off to the given message handler.
	 * 
	 * @author Chase Covello Philip Russell
	 * 
	 */
	private final class UpdateThread extends Thread {

		private MessageHandler handler;

		private ServerSocket listener;

		/**
		 * Construct a new UpdateThread with the given message handler.
		 * 
		 * @param handler
		 *            the message handler to use.
		 * @throws IOException
		 *             in the case of unrecoverable network errors.
		 */
		public UpdateThread(MessageHandler handler) throws IOException {
			this.handler = handler;
			listener = new ServerSocket(0);
			listener.setSoTimeout(1000);
		}

		/**
		 * The main loop of this thread. When running is set to false, the
		 * thread terminates.
		 */
		public void run() {
			while (running) {
				try {
					Socket socket = listener.accept();

					ObjectInputStream ois = new ObjectInputStream(socket
							.getInputStream());
					Message message = (Message) ois.readObject();
					socket.close();

					handler.receive(message);
				} catch (IOException e) {
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
	}

	// when this flag is false, UpdateThread terminates
	private boolean running = true;

	/**
	 * The thread that listens for incoming TCP connections.
	 */
	protected final UpdateThread ut;

	/**
	 * Constructs a new MessageReceiver with the given message handler. An
	 * UpdateThread is started to listen for incoming connections.
	 * 
	 * @param handler
	 *            the message handler to use.
	 * @throws IOException
	 *             in the case of unrecoverable network errors.
	 */
	public MessageReceiver(MessageHandler handler) throws IOException {
		ut = new UpdateThread(handler);
		ut.start();
	}

	/**
	 * Returns the socket address that this receiver is listening on.
	 * 
	 * @return the socket address.
	 */
	public SocketAddress getSocketAddress() {
		return ut.listener.getLocalSocketAddress();
	}

	/**
	 * Sets this message receiver's handler to the given message handler.
	 * 
	 * @param handler
	 *            the message handler to use.
	 */
	public void setMessageHandler(MessageHandler handler) {
		ut.handler = handler;
	}

	/**
	 * Shuts down any threads created by this message receiver. This method
	 * <b>must</b> be called before disposing of all references to this object;
	 * otherwise, its thread will continue to run until the application is
	 * forcibly terminated.
	 */
	public void shutdown() {
		running = false;
	}
}
